﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
using Windows.Storage.Streams;

namespace BoriPetr.OAuth
{
    public abstract class OAuthMessageBase<T>
    {
        #region Constructor
        public OAuthMessageBase(OAuthSettings _settings, OAuthToken _token)
        {
            settings = _settings;
            token = _token;
            isMessageSigned = false;
            urlBase = _settings.MessageUrlBase;
            AddRequestParameter(OAUTH_CONSUMER_KEY, settings.ConsumerKey);
            AddRequestParameter(OAUTH_VERSION, settings.VersionValue);
            AddRequestParameter(OAUTH_NONCE, GenerateNonce());
            AddRequestParameter(OAUTH_TIMESTAMP, GenerateTimestamp());
            AddRequestParameter(OAUTH_SIGNATURE_METHOD, settings.SignatureMethod);
        }
        #endregion

        #region Variables

        protected const string OAUTH_CONSUMER_KEY = "oauth_consumer_key";
        protected const string OAUTH_SIGNATURE_METHOD = "oauth_signature_method";
        protected const string OAUTH_TIMESTAMP = "oauth_timestamp";
        protected const string OAUTH_NONCE = "oauth_nonce";
        protected const string OAUTH_VERSION = "oauth_version";
        protected const string OAUTH_TOKEN = "oauth_token";

        protected string urlBase;
        private OAuthSettings settings;
        private OAuthToken token;
        private bool isMessageSigned;

        private string parameterUri;
        private string ParameterUri
        {
            get
            {
                if (!isMessageSigned)
                {
                    parameterUri = "";
                    bool isFirst = true;
                    var list = ParameterDictionary.Keys.ToList();
                    list.Sort();

                    foreach (var key in list)
                    {
                        if (isFirst) isFirst = false;
                        else parameterUri += "&";
                        parameterUri += Uri.EscapeDataString(key) + "=" + Uri.EscapeDataString(ParameterDictionary[key]);
                    }
                }
                return parameterUri;
            }
        }

        private Dictionary<string, string> parameterDictionary;
        private Dictionary<string, string> ParameterDictionary
        {
            get
            {
                if (parameterDictionary == null)
                    parameterDictionary = new Dictionary<string, string>();
                return parameterDictionary;
            }
            set { parameterDictionary = value; }
        }

        private string requestUrl;
        protected string RequestUrl
        {
            get
            {
                if (!isMessageSigned)
                    GenerateRequestUrl();
                return requestUrl;
            }
            private set
            {
                requestUrl = value;
            }
        }

        #endregion


        private void GenerateRequestUrl()
        {
            if (!isMessageSigned)
            {
                String SigBaseString = "GET&";
                SigBaseString += Uri.EscapeDataString(urlBase) + "&" + Uri.EscapeDataString(ParameterUri);

                IBuffer KeyMaterial = CryptographicBuffer.ConvertStringToBinary(settings.SharedSecret + "&" + token.Secret, BinaryStringEncoding.Utf8);
                MacAlgorithmProvider HmacSha1Provider = MacAlgorithmProvider.OpenAlgorithm("HMAC_SHA1");
                CryptographicKey MacKey = HmacSha1Provider.CreateKey(KeyMaterial);
                IBuffer DataToBeSigned = CryptographicBuffer.ConvertStringToBinary(SigBaseString, BinaryStringEncoding.Utf8);
                IBuffer SignatureBuffer = CryptographicEngine.Sign(MacKey, DataToBeSigned);
                String Signature = CryptographicBuffer.EncodeToBase64String(SignatureBuffer);

                RequestUrl = urlBase + "?" + ParameterUri + "&oauth_signature=" + Uri.EscapeDataString(Signature);
                isMessageSigned = true;
            }
            else
            {
                //throw new FatSecretException(FatSecretException.RESIGN_NOT_ALLOWED);
            }
        }

        protected string GenerateNonce()
        {
            Random Rand = new Random();
            Int32 Nonce = Rand.Next(1000000000);
            return Nonce.ToString();
        }

        protected string GenerateTimestamp()
        {
            TimeSpan SinceEpoch = DateTime.UtcNow - new DateTime(1970, 1, 1);
            var totalSeconds = Math.Round(SinceEpoch.TotalSeconds);
            return totalSeconds.ToString();
        }

        protected string DateToDaysSince1970(DateTime date)
        {
            TimeSpan SinceEpoch = date - new DateTime(1970, 1, 1);
            var totalDays = Math.Round(SinceEpoch.TotalDays); /* -1 ???? */
            return totalDays.ToString();
        }

        protected void AddRequestParameter(string name, object value)
        {
            string stringValue="";
            if (value is DateTime) stringValue = DateToDaysSince1970((DateTime)value);
            else stringValue = value.ToString();

            ParameterDictionary.Add(name, stringValue);
        }

        public async Task<T> SendAsync()
        {
            string response = "";
            try
            {
                using (HttpClient httpClient = new HttpClient())
                {
                    response = await httpClient.GetStringAsync(RequestUrl);
                }
            }
            catch (Exception)
            {
                //rootPage.NotifyUser("Error getting data from server." + Err.Message, NotifyType.StatusMessage);
            }
            return (T)ProcessResponse(response);
        }

        protected abstract T ProcessResponse(string response);
    }
}
